package org.datascooter.db.mysql;

import java.text.MessageFormat;

import org.datascooter.bundle.DSMapper;
import org.datascooter.bundle.EntityBundle;
import org.datascooter.bundle.attribute.PersistAttribute;
import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.impl.Snip;
import org.datascooter.impl.SnipBuilder;
import org.datascooter.inface.ISnipBuilder;
import org.datascooter.meta.Column;
import org.datascooter.meta.MetaLink;
import org.datascooter.utils.DBType;
import org.datascooter.utils.DSSettings;
import org.datascooter.utils.LangUtils;
import org.datascooter.utils.SnipUtils;
import org.datascooter.utils.policy.SnipType;

public class MysqlSnipBuilder extends SnipBuilder {

	private static final String CREATE_1_INDEX_IDX_ON_2_3 = "CREATE {1} INDEX IDX_{0}  ON {2}({3})";
	private static final String ALTER_TABLE_0_DROP_FOREIGN_KEY_1 = "ALTER TABLE {0} DROP FOREIGN KEY {1}";
	private static final String ALTER_TABLE_0_MODIFY_COLUMN_1 = "ALTER TABLE {0} MODIFY COLUMN {1}";
	private static final String BIT = "BIT";
	private static final String TINYINT = "TINYINT";
	private static final String _22_2 = "(22,2)";
	private static final String MEDIUMTEXT = "MEDIUMTEXT";
	private static final String MEDIUMBLOB = "MEDIUMBLOB";
	private static final String CHAR = "CHAR";
	private static final String VARCHAR = "VARCHAR";
	private static final String INT = "INT";
	private static final String BOOLEAN = "BOOLEAN";
	private static final String BIGINT = "BIGINT";
	private static final String DOUBLE = "DOUBLE";
	private static final String DATE = "DATE";
	private static final String DATETIME = "DATETIME";

	public MysqlSnipBuilder() {
		super("mysql");
	}

	@Override
	public ISnipBuilder copy() {
		return new MysqlSnipBuilder();
	}

	// @Override
	// public String managePage( String sqlClause) {
	// if (dataSnip.getFetchSize() > 0) {
	// paramList.add(dataSnip.getFromRow() - 1);
	// paramList.add(dataSnip.getFetchSize());
	// return sqlClause + " LIMIT ? , ?";
	// }
	// return sqlClause;
	// }
	@Override
	public Snip buildUpdateColumnSnip(String entity, String columName, String typeName, int scale, int precision, boolean nullable) {
		EntityBundle bundle = DSMapper.getBundle(entity);
		String size = buildDataLength(scale, precision);
		String str = SnipUtils.SP + columName + SnipUtils.SP + typeName + SnipUtils.SP + size + SnipUtils.SP + (nullable ? SnipUtils.NN : SnipUtils.NOT_NULL);
		return new Snip(null, SnipType.EXECUTE, MessageFormat.format(ALTER_TABLE_0_MODIFY_COLUMN_1, bundle.getTableName(), str), null, null, null, -1, null);
	}

	@Override
	public Snip buildUpdateColumnSnip(String entity, PersistAttribute attribute) throws EntityNotMappedException, SecurityException, NoSuchMethodException {
		EntityBundle bundle = DSMapper.getBundle(entity);
		return new Snip(null, SnipType.EXECUTE, MessageFormat.format(ALTER_TABLE_0_MODIFY_COLUMN_1, bundle.getTableName(), buildColumnClause(attribute)), null, null, null, -1, null);
	}

	@Override
	public String dropKey(MetaLink link) {
		String keyClause = MessageFormat.format(ALTER_TABLE_0_DROP_FOREIGN_KEY_1, link.FKTABLE_NAME, link.FK_NAME);
		return keyClause;
	}

	protected void buildColumnIndex(StringBuilder indexBuilder, String name, String type, String column, String tableName) {
		if (indexBuilder.length() > 0) {
			indexBuilder.append(SnipUtils.SEMICOLON + SnipUtils.NL);
		}
		indexBuilder.append(MessageFormat.format(
			CREATE_1_INDEX_IDX_ON_2_3,
			LangUtils.defaultIfEmpty(name, SnipUtils.NN).toUpperCase() + LangUtils.getRandomIndexString(3),
			LangUtils.defaultIfEmpty(type),
			tableName,
			column));
	}

	@Override
	public String getCaseSensitiveTableName(String tableName) {
		return tableName;
	}

	@SuppressWarnings("nls")
	@Override
	public String buildColumnClause(PersistAttribute attribute) {
		DBType type = attribute.getType();
		String typeStr = getDbTypeString(type);
		String size = buildDataLength(attribute.getScale(), attribute.getPrecision());
		switch (type) {
			case BIGDECIMAL:
				typeStr = DOUBLE;
				if (size.length() == 0) {
					size = _22_2;
				}
				break;
			case LONG:
				typeStr = BIGINT;
				size = SnipUtils.NN;
				break;
			case BOOLEAN:
				typeStr = BOOLEAN;
				size = SnipUtils.NN;
				break;
			case INT:
				typeStr = INT;
				size = SnipUtils.NN;
				break;
			case STRING:
				typeStr = VARCHAR;
				if (size.length() == 0) {
					size = SnipUtils.LB + DSSettings.getDefaultStringLength() + SnipUtils.RB;
				}
				break;
			case CHAR:
				typeStr = CHAR;
				if (size.length() == 0) {
					size = SnipUtils.LB + DSSettings.getDefaultStringLength() + SnipUtils.RB;
				}
				break;
			case BLOB:
				typeStr = MEDIUMBLOB;
				size = SnipUtils.NN;
				break;
			case CLOB:
				typeStr = MEDIUMTEXT;
				size = SnipUtils.NN;
				break;
			case TIMESTAMP:
				typeStr = DATETIME;
				if (size.length() == 0) {
					size = SnipUtils.NN;
				}
				break;
		}
		return SnipUtils.SP + attribute.getColumnName() + SnipUtils.SP + typeStr + SnipUtils.SP + size + (attribute.isNullable() ? SnipUtils.NN : SnipUtils.NOT_NULL);
	}

	@Override
	public boolean equalType(PersistAttribute attribute, Column column) {
		boolean result = false;
		boolean scaled = true;
		if (attribute.getType() == null) {
			return true;
		}
		switch (attribute.getType()) {
			case BIGDECIMAL:
				result = DOUBLE.equalsIgnoreCase(column.dataTypeName);
				break;
			case LONG:
				result = BIGINT.equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case BOOLEAN:
				result = TINYINT.equalsIgnoreCase(column.dataTypeName) || BIT.equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case INT:
				result = INT.equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case STRING:
				result = VARCHAR.equalsIgnoreCase(column.dataTypeName);
				break;
			case CHAR:
				result = CHAR.equalsIgnoreCase(column.dataTypeName);
				break;
			case BLOB:
				result = MEDIUMBLOB.equalsIgnoreCase(column.dataTypeName);
				break;
			case CLOB:
				result = MEDIUMTEXT.equalsIgnoreCase(column.dataTypeName);
				break;
			case TIMESTAMP:
				result = DATE.equalsIgnoreCase(column.dataTypeName) || DATETIME.equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
		}
		if (!attribute.getType().equals(DBType.BLOB) && !attribute.getType().equals(DBType.CLOB) && !attribute.getType().equals(DBType.TIMESTAMP)) {
			result = checkScale(attribute, column, result, scaled);
		}
		return result;
	}
}
